package com.zb.excel.importExcel.listener;

import cn.hutool.core.collection.CollectionUtil;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.metadata.data.ReadCellData;
import com.alibaba.excel.read.listener.ReadListener;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.zb.constant.RedisImportDataConstant;
import com.zb.entity.*;
import com.zb.excel.NetworkEmpImport;
import com.zb.redis.RedisUtil;
import com.zb.secure.BCryptUtil;
import com.zb.service.*;
import com.zb.utils.TransactionUtil;
import com.zb.utils.VerificUtil;
import com.zb.utils.ZbExcelFieldUtil;
import com.zb.websocket.server.ProgressWebSocketServer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionStatus;

import java.util.*;

/**
 * @Author WEN
 * @Date 2024/2/26 0026
 **/
public class NetworkEmpImportListener implements ReadListener<NetworkEmpImport> {
    private static final Logger logger = LoggerFactory.getLogger(NetworkEmpImportListener.class);
    private static int BATCH_COUNT = 300; // 每批次执行插入数据库数量
    private boolean isFirstSend; // 是否首次发送进度
    private boolean isTwoSend; // 是否二次发送进度
    private boolean isEndSend; // 是否最后一次发送进度
    private int excelTotalRowCount; // Excel总数量（此包含空行的数量）
    private int totalCount; // 导入的总数量
    private int batch_no; // 导入的批次号
    private long currentExcuteCount; // 当前执行的数量
    // 校验数据重复的缓存key
    private String check_data_repeat_key = RedisImportDataConstant.EXCEL + "check-network-emp-data-repeat:";
    private Map<String, Object> dataRepeatMap;
    private boolean isSendMessage; // 是否已发送消息
    private String progressUuid; // 进度uuid识别码
    private AccounLoginInfo loginInfo;
    private List<NetworkEmpImport> importFailt;
    private StringBuffer message;
    private String CACHE_IMPORT_DATA_REDIS_KEY = RedisImportDataConstant.NETWORK_EMP_IMPORT;

    private List<ZjNetworkEmpLevel> empLevelList;
    private List<ZjNetworkEmpSkill> empSkillList;
    private List<ZjNetworkEmpType> empTypeList;
    private List<ZjAccount> addAccountList;
    private List<ZjAccount> updateAccountList;
    private List<ZjNetworkEmp> networkEmpList;
    private List<ZjNetworkEmpLevelRelation> empLevelRelationList;
    private List<ZjNetworkEmpSkillRelation> empSkillRelationList;
    private List<ZjNetworkEmpRelation> addNetworkEmpRelationList;
    private List<ZjNetworkEmpRelation> updateNetworkEmpRelationList;

    private PlatformTransactionManager transactionManager;
    private ZjNetworkEmpLevelService networkEmpLevelService;
    private ZjNetworkEmpSkillService networkEmpSkillService;
    private ZjNetworkEmpTypeService networkEmpTypeService;
    private ZjAccountService accountService;
    private ZjNetworkEmpService networkEmpService;
    private ZjNetworkEmpLevelRelationService networkEmpLevelRelationService;
    private ZjNetworkEmpSkillRelationService networkEmpSkillRelationService;
    private ZjNetworkEmpRelationService networkEmpRelationService;

    public NetworkEmpImportListener(String progressUuid, AccounLoginInfo loginInfo, PlatformTransactionManager transactionManager, ZjNetworkEmpLevelService networkEmpLevelService,
                                    ZjNetworkEmpSkillService networkEmpSkillService, ZjNetworkEmpTypeService networkEmpTypeService, ZjAccountService accountService, ZjNetworkEmpService networkEmpService,
                                    ZjNetworkEmpLevelRelationService networkEmpLevelRelationService, ZjNetworkEmpSkillRelationService networkEmpSkillRelationService,
                                    ZjNetworkEmpRelationService networkEmpRelationService) {
        this.isFirstSend = true;
        this.isTwoSend = true;
        this.isEndSend = false;
        this.excelTotalRowCount = 0;
        this.totalCount = 0;
        this.batch_no = 0;
        this.currentExcuteCount = 0;
        this.isSendMessage = false;
        this.check_data_repeat_key = check_data_repeat_key + loginInfo.getCustAccId() + ":" + this.progressUuid;

        this.progressUuid = progressUuid;
        this.loginInfo = loginInfo;
        this.importFailt = new ArrayList<>();
        this.message = new StringBuffer();
        this.CACHE_IMPORT_DATA_REDIS_KEY = this.CACHE_IMPORT_DATA_REDIS_KEY + loginInfo.getCustAccId() + this.progressUuid;

        this.transactionManager = transactionManager;
        this.networkEmpLevelService = networkEmpLevelService;
        this.networkEmpSkillService = networkEmpSkillService;
        this.networkEmpTypeService = networkEmpTypeService;
        this.accountService = accountService;
        this.networkEmpService = networkEmpService;
        this.networkEmpLevelRelationService = networkEmpLevelRelationService;
        this.networkEmpSkillRelationService = networkEmpSkillRelationService;
        this.networkEmpRelationService = networkEmpRelationService;
        init();
    }

    private void init() {
        this.empLevelList = new ArrayList<>();
        this.empSkillList = new ArrayList<>();
        this.empTypeList = new ArrayList<>();
        this.addAccountList = new ArrayList<>();
        this.updateAccountList = new ArrayList<>();
        this.networkEmpList = new ArrayList<>();
        this.empLevelRelationList = new ArrayList<>();
        this.empSkillRelationList = new ArrayList<>();
        this.addNetworkEmpRelationList = new ArrayList<>();
        this.updateNetworkEmpRelationList = new ArrayList<>();
    }

    @Override
    public void invokeHead(Map<Integer, ReadCellData<?>> headMap, AnalysisContext context) {
        this.excelTotalRowCount = context.readSheetHolder().getApproximateTotalRowNumber();
        this.totalCount = this.excelTotalRowCount -2;
        if (this.isFirstSend) {
            this.message.append("数据正在解析中，请稍等...").append("</br>");
            ProgressWebSocketServer.send(this.progressUuid, false,  new ArrayList(), this.message.toString());
            this.isFirstSend = false;
        }
    }

    @Override
    public void invoke(NetworkEmpImport empImport, AnalysisContext analysisContext) {
        if (this.isTwoSend) {
            this.message.append("数据解析成功，正在导入中..").append("</br>");
            ProgressWebSocketServer.send(this.progressUuid, false,  new ArrayList(), this.message.toString());
            this.isTwoSend = false;
        }
        this.currentExcuteCount++;
        analysisContext.readWorkbookHolder().setIgnoreEmptyRow(false);
        int rowNo = analysisContext.readRowHolder().getRowIndex() + 1; // Excel行号
        if (ZbExcelFieldUtil.isLineNullValue(empImport)) {
            this.totalCount--;
            return;
        }
        if (checkEmpty(rowNo, empImport)) {
            sendMessage();
            return;
        }
        if (checkDataRepeat(rowNo, empImport)) {
            sendMessage();
            return;
        }
        Map<String, Object> mapKey = getCacheData();
        // 所属服务商
        if (null == mapKey.get("network_" + empImport.getNetwork())) {
            this.importFailt.add(empImport);
            message.append("E").append(rowNo).append("单元 所属服务商不存在系统中：").append(empImport.getNetwork()).append("</br>");
            sendMessage();
            return;
        }
        ZjNetwork network = JSON.parseObject(JSON.toJSONString(mapKey.get("network_" + empImport.getNetwork())), ZjNetwork.class);
        // 工程师等级
        ZjNetworkEmpLevel empLevel = null;
        if (null != mapKey.get("network_emp_level_" + empImport.getNetworkEmpLevel())) {
            empLevel = JSON.parseObject(JSON.toJSONString(mapKey.get("network_emp_level_" + empImport.getNetworkEmpLevel())), ZjNetworkEmpLevel.class);
        } else {
            empLevel = new ZjNetworkEmpLevel();
            empLevel.setId(IdWorker.getId());
            empLevel.setCustAccId(loginInfo.getCustAccId());
            empLevel.setName(empImport.getNetworkEmpLevel());
            empLevel.setDisabled(false);
            this.empLevelList.add(empLevel);
            mapKey.put("network_emp_level_" + empImport.getNetworkEmpLevel(), empLevel);
            RedisUtil.setCache(this.CACHE_IMPORT_DATA_REDIS_KEY, JSON.toJSONString(mapKey));
        }
        // 工程师技能
        ZjNetworkEmpSkill empSkill = null;
        if (StringUtils.isNotBlank(empImport.getNetworkEmpSkill())) {
            if (null != mapKey.get("network_emp_skill_" + empImport.getNetworkEmpSkill())) {
                empSkill = JSON.parseObject(JSON.toJSONString(mapKey.get("network_emp_skill_" + empImport.getNetworkEmpSkill())), ZjNetworkEmpSkill.class);
            } else {
                empSkill = new ZjNetworkEmpSkill();
                empSkill.setId(IdWorker.getId());
                empSkill.setCustAccId(loginInfo.getCustAccId());
                empSkill.setName(empImport.getNetworkEmpSkill());
                empSkill.setDisabled(false);
                this.empSkillList.add(empSkill);
                mapKey.put("network_emp_skill_" + empImport.getNetworkEmpSkill(), empSkill);
                RedisUtil.setCache(this.CACHE_IMPORT_DATA_REDIS_KEY, JSON.toJSONString(mapKey));
            }
        }
        // 工程师分类
        ZjNetworkEmpType empType = null;
        if (null != mapKey.get("network_emp_type_" + empImport.getNetworkEmpType())) {
            empType = JSON.parseObject(JSON.toJSONString(mapKey.get("network_emp_type_" + empImport.getNetworkEmpType())), ZjNetworkEmpType.class);
        } else {
            empType = new ZjNetworkEmpType();
            empType.setId(IdWorker.getId());
            empType.setCustAccId(loginInfo.getCustAccId());
            empType.setName(empImport.getNetworkEmpType());
            empType.setDisabled(false);
            this.empTypeList.add(empType);
            mapKey.put("network_emp_type_" + empImport.getNetworkEmpType(), empType);
            RedisUtil.setCache(this.CACHE_IMPORT_DATA_REDIS_KEY, JSON.toJSONString(mapKey));
        }
        // 工程师账号
        ZjAccount account = null;
        if (null != mapKey.get("network_emp_account_"+empImport.getPhone())) {
            account = JSON.parseObject(JSON.toJSONString(mapKey.get("network_emp_account_" + empImport.getPhone())), ZjAccount.class);
            account.setName(empImport.getUsername());
            this.updateAccountList.add(account);
        } else {
            account = new ZjAccount();
            account.setId(IdWorker.getId());
            account.setSalt(BCryptUtil.generatorSalt());
            account.setAccount("ZB_" + BCryptUtil.generatorSalt().substring(10, 20));
            account.setPhone(empImport.getPhone());
            account.setPassword(BCryptUtil.encryPassword(BCryptUtil.MD5(empImport.getPhone().substring(5)), account.getSalt()));
            account.setName(empImport.getUsername());
            account.setNickname(empImport.getPhone().substring(6));
            account.setAvatar("");
            account.setStatus(0);
            this.addAccountList.add(account);
            mapKey.put("network_emp_account_" + empImport.getPhone(), account);
            RedisUtil.setCache(this.CACHE_IMPORT_DATA_REDIS_KEY, JSON.toJSONString(mapKey));
        }
        // 工程师人员
        ZjNetworkEmp networkEmp = null;
        if (null != mapKey.get("network_emp_" + account.getId())) {
            networkEmp = JSON.parseObject(JSON.toJSONString(mapKey.get("network_emp_" + account.getId())), ZjNetworkEmp.class);
        } else {
            networkEmp = new ZjNetworkEmp();
            networkEmp.setId(IdWorker.getId());
            networkEmp.setAutoSureReceiveOrder(true);
            networkEmp.setAccountId(account.getId());
            this.networkEmpList.add(networkEmp);
            mapKey.put("network_emp_" + account.getId(), networkEmp);
            RedisUtil.setCache(this.CACHE_IMPORT_DATA_REDIS_KEY, JSON.toJSONString(mapKey));
        }
        // 工程师等级关联表
        ZjNetworkEmpLevelRelation empLevelRelation = null;
        if (null == mapKey.get("network_emp_level_relation_" + loginInfo.getCustAccId() + networkEmp.getId())) {
            empLevelRelation = new ZjNetworkEmpLevelRelation();
            empLevelRelation.setId(IdWorker.getId());
            empLevelRelation.setCustAccId(loginInfo.getCustAccId());
            empLevelRelation.setNetworkEmpId(networkEmp.getId());
            empLevelRelation.setNetworkEmpLevelId(empLevel.getId());
            this.empLevelRelationList.add(empLevelRelation);
            mapKey.put("network_emp_level_relation_" + loginInfo.getCustAccId() + networkEmp.getId(), true);
            RedisUtil.setCache(this.CACHE_IMPORT_DATA_REDIS_KEY, JSON.toJSONString(mapKey));
        }
        // 工程师技能关联表
        if (null != empSkill) {
            Long empSkillId = null;
            if (null != mapKey.get("network_emp_skill_relation_" + loginInfo.getCustAccId() + networkEmp.getId() + empSkill.getId())) {
                empSkillId = JSON.parseObject(JSON.toJSONString(mapKey.get("network_emp_level_relation_" + loginInfo.getCustAccId() + networkEmp.getId() + empSkill.getId())), Long.class);
            }
            if (null == empSkillId || !Objects.equals(empSkill.getId(), empSkillId)) {
                ZjNetworkEmpSkillRelation empSkillRelation = new ZjNetworkEmpSkillRelation();
                empSkillRelation.setId(IdWorker.getId());
                empSkillRelation.setCustAccId(loginInfo.getCustAccId());
                empSkillRelation.setNetworkEmpId(networkEmp.getId());
                empSkillRelation.setNetworkEmpSkillId(empSkill.getId());
                this.empSkillRelationList.add(empSkillRelation);
                mapKey.put("network_emp_skill_relation_" + loginInfo.getCustAccId() + networkEmp.getId() + empSkill.getId(), empSkill.getId());
                RedisUtil.setCache(this.CACHE_IMPORT_DATA_REDIS_KEY, JSON.toJSONString(mapKey));
            }
        }
        // 关联服务商
        if (null == mapKey.get("network_emp_relation_" + network.getId() + networkEmp.getId())) {
            ZjNetworkEmpRelation networkEmpRelation = new ZjNetworkEmpRelation();
            networkEmpRelation.setId(IdWorker.getId());
            networkEmpRelation.setCustAccId(loginInfo.getCustAccId());
            networkEmpRelation.setNetworkId(network.getId());
            networkEmpRelation.setNetworkEmpId(networkEmp.getId());
            networkEmpRelation.setSuperManager(empImport.getSuperManager());
            networkEmpRelation.setManager(false);
            this.addNetworkEmpRelationList.add(networkEmpRelation);
            mapKey.put("network_emp_relation_" + network.getId() + networkEmp.getId(), networkEmpRelation);
            RedisUtil.setCache(this.CACHE_IMPORT_DATA_REDIS_KEY, JSON.toJSONString(mapKey));
        } else {
            ZjNetworkEmpRelation networkEmpRelation = JSON.parseObject(JSON.toJSONString(mapKey.get("network_emp_relation_" + network.getId() + networkEmp.getId())), ZjNetworkEmpRelation.class);
            if (!networkEmpRelation.getManager()) {
                networkEmpRelation.setSuperManager(empImport.getSuperManager());
                this.updateNetworkEmpRelationList.add(networkEmpRelation);
            }
        }
        if (addAccountList.size() >= BATCH_COUNT || updateAccountList.size() >= BATCH_COUNT) {
            excuteSave();
        }
        sendMessage();
    }

    private boolean checkDataRepeat(int rowNo, NetworkEmpImport empImport) {
        boolean isDataRepeat = false;
        Object byKey = RedisUtil.getByKey(check_data_repeat_key);
        dataRepeatMap = JSON.parseObject((String) byKey, HashMap.class);
        if (null == dataRepeatMap) {
            dataRepeatMap = new HashMap<>();
        }
        if (null != dataRepeatMap.get("network_emp_account_" + empImport.getPhone())) {
            message.append("B").append(rowNo).append("单元 工程师手机号已重复填写").append("</br>");
            isDataRepeat = true;
        } else {
            dataRepeatMap.put("network_emp_account_" + empImport.getPhone(), true);
        }
        RedisUtil.setCache(check_data_repeat_key, JSON.toJSONString(dataRepeatMap));
        if (isDataRepeat) {
            this.importFailt.add(empImport);
        }
        return isDataRepeat;
    }

    private boolean checkEmpty(int rowNo, NetworkEmpImport empImport) {
        if (StringUtils.isBlank(empImport.getUsername())) {
            this.importFailt.add(empImport);
            message.append("A").append(rowNo).append("单元 工程师姓名不能为空").append("</br>");
            return true;
        }
        if (empImport.getUsername().length() > 100) {
            this.importFailt.add(empImport);
            message.append("A").append(rowNo).append("单元 工程师长度限制100个字符").append("</br>");
            return true;
        }
        if (!VerificUtil.phoneVerific(empImport.getPhone())) {
            this.importFailt.add(empImport);
            message.append("B").append(rowNo).append("单元 工程师手机号格式错误").append("</br>");
            return true;
        }
        if (null == empImport.getSuperManager()) {
            this.importFailt.add(empImport);
            message.append("C").append(rowNo).append("单元 管理员权限不能为空").append("</br>");
            return true;
        }
        if (StringUtils.isBlank(empImport.getNetworkEmpLevel())) {
            this.importFailt.add(empImport);
            message.append("D").append(rowNo).append("单元 工程师等级不能为空").append("</br>");
            return true;
        }
        if (empImport.getNetworkEmpLevel().length() > 10) {
            this.importFailt.add(empImport);
            message.append("D").append(rowNo).append("单元 工程师等级名称长度限制10个字符").append("</br>");
            return true;
        }
        if (StringUtils.isBlank(empImport.getNetworkEmpType())) {
            this.importFailt.add(empImport);
            message.append("E").append(rowNo).append("单元 工程师分类不能为空").append("</br>");
            return true;
        }
        if (empImport.getNetworkEmpType().length() > 20) {
            this.importFailt.add(empImport);
            message.append("E").append(rowNo).append("单元 工程师分类名称长度限制20个字符").append("</br>");
            return true;
        }
        if (StringUtils.isBlank(empImport.getNetwork())) {
            this.importFailt.add(empImport);
            message.append("F").append(rowNo).append("单元 所属服务商不能为空").append("</br>");
            return true;
        }
        if (StringUtils.isNotBlank(empImport.getNetworkEmpSkill()) && empImport.getNetworkEmpSkill().length() > 10) {
            this.importFailt.add(empImport);
            message.append("G").append(rowNo).append("单元 工程师技能名称长度限制10个字符").append("</br>");
            return true;
        }
        return false;
    }


    @Override
    public void onException(Exception exception, AnalysisContext context) throws Exception {
        RedisUtil.clearCache(check_data_repeat_key);
    }

    @Override
    public void doAfterAllAnalysed(AnalysisContext analysisContext) {
        RedisUtil.clearCache(check_data_repeat_key);
        if (CollectionUtil.isNotEmpty(addAccountList) || CollectionUtil.isNotEmpty(updateAccountList)) {
            excuteSave();
        }
        if (!this.isEndSend) {
            ProgressWebSocketServer.send(this.progressUuid, true, 100, this.totalCount, this.importFailt, this.message.toString());
            this.isEndSend = true;
        }
    }

    /**
     * 获取缓存的数据
     * @return
     */
    private Map<String, Object> getCacheData() {
        Map<String, Object> cacheData = null;
        Object byKey = RedisUtil.getByKey(this.CACHE_IMPORT_DATA_REDIS_KEY);
        if (null == byKey) {
            return new HashMap<>();
        }
        cacheData = JSON.parseObject((String) byKey, Map.class);
        return cacheData;
    }

    private void excuteSave() {
        String color = "#67C23A";
        this.batch_no++;
        this.message.append("<div style=color:").append(color).append(">解析完成，正在处理保存 [第 ").append(this.batch_no).append("批] 数据，请耐心等待...</div>");
        sendMessage();
        this.message.append("<div style=color:").append(color).append(">第 ").append(this.batch_no);
        TransactionStatus status = this.transactionManager.getTransaction(TransactionUtil.transactionDefinition());
        try {
            if (CollectionUtil.isNotEmpty(empLevelList)) this.networkEmpLevelService.saveBatch(empLevelList);
            if (CollectionUtil.isNotEmpty(empSkillList)) this.networkEmpSkillService.saveBatch(empSkillList);
            if (CollectionUtil.isNotEmpty(empTypeList)) this.networkEmpTypeService.saveBatch(empTypeList);
            if (CollectionUtil.isNotEmpty(addAccountList)) this.accountService.saveBatch(addAccountList);
            if (CollectionUtil.isNotEmpty(updateAccountList)) this.accountService.updateBatchById(updateAccountList);
            if (CollectionUtil.isNotEmpty(networkEmpList)) this.networkEmpService.saveBatch(networkEmpList);
            if (CollectionUtil.isNotEmpty(empLevelRelationList)) this.networkEmpLevelRelationService.saveBatch(empLevelRelationList);
            if (CollectionUtil.isNotEmpty(empSkillRelationList)) this.networkEmpSkillRelationService.saveBatch(empSkillRelationList);
            if (CollectionUtil.isNotEmpty(addNetworkEmpRelationList)) this.networkEmpRelationService.saveBatch(addNetworkEmpRelationList);
            if (CollectionUtil.isNotEmpty(updateNetworkEmpRelationList)) this.networkEmpRelationService.updateBatchById(updateNetworkEmpRelationList);
            this.transactionManager.commit(status);
            this.message.append(" 批导入成功</div>");
        }catch (Exception e) {
            e.printStackTrace();
            color = "#F56C6C";
            logger.error("工程师第 {} 批导入失败，失败原因：{}", this.batch_no, e.getMessage());
            this.message.append(" 导入失败！原因：").append(e.getMessage()).append("</div>");
            this.transactionManager.rollback(status);
        }
        init();
    }

    /**
     * 发送进度消息
     */
    private void sendMessage() {
        ProgressWebSocketServer.send(this.progressUuid, false, (1.0 * this.currentExcuteCount / this.excelTotalRowCount *100), this.importFailt, this.message.toString());
    }

}
